#include "Poco/Exception.h"
#include "Poco/File.h"
#include "Poco/String.h"
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <vector>


struct VSVersion
{
    VSVersion(const std::string & inVersion,
              const std::string & inProjectExtension,
              const std::string & inSolutionExtension,
              const std::string & inPostFix) :
        mVersion(inVersion),
        mProjectExtension(inProjectExtension),
        mSolutionExtension(inSolutionExtension),
        mPostFix(inPostFix)
    {
    }

    std::string mVersion;
    std::string mProjectExtension;
    std::string mSolutionExtension;
    std::string mPostFix;
};


std::vector<VSVersion> gVSVersions;


void ReadEntireFile(const std::string & inPath, std::string & str)
{
    try
    {
        std::ifstream t(inPath.c_str());

        t.seekg(0, std::ios::end);   
        str.reserve(t.tellg());
        t.seekg(0, std::ios::beg);

        str.assign((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
    }
    catch (const std::exception & inException)
    {
        // We rethrow this exception because we want to add a more useful error message.
        throw std::runtime_error("Could not read " + inPath + " (error: " + inException.what() + ").");
    }
}


void CreateTemplateOffspring(const std::string & inTemplateFile, const std::string & inProjectName)
{    
    // Generate the string with replaced tags
    std::string translation;
    ReadEntireFile(inTemplateFile, translation);
    translation = Poco::replace<std::string>(translation, "Sample", inProjectName);

    // Write the string to the output file
    std::string outFile = Poco::replace<std::string>(inTemplateFile, "Sample", inProjectName);
    std::ofstream of(outFile.c_str());
    of << translation;
}


void CreateSolutionFile(const std::string & inProjectName)
{
    for (size_t idx = 0; idx < gVSVersions.size(); ++idx)
    {
        const VSVersion & vs = gVSVersions[idx];
        const std::string cSrcFile = "Sample_VS_POSTFIX.VS_SOLUTION_EXTENSION";
        std::string srcFile = Poco::replace<std::string>(cSrcFile, "VS_POSTFIX", vs.mPostFix);
        srcFile = Poco::replace<std::string>(srcFile, "VS_SOLUTION_EXTENSION", vs.mSolutionExtension);
        CreateTemplateOffspring(srcFile, inProjectName);
    }
}


void CreateSolutionDirectory(const std::string & inProjectName)
{
    std::string command = "svn export Sample " + inProjectName;
    system(command.c_str());

    for (size_t idx = 0; idx < gVSVersions.size(); ++idx)
    {
        const VSVersion & vs = gVSVersions[idx];
        const std::string cSrcFile = "PROJECT_NAME\\Sample_VS_POSTFIX.VS_PROJECT_EXTENSION";
        std::string srcFile = Poco::replace<std::string>(cSrcFile, "PROJECT_NAME", inProjectName);
        srcFile = Poco::replace<std::string>(srcFile, "VS_POSTFIX", vs.mPostFix);
        srcFile = Poco::replace<std::string>(srcFile, "VS_PROJECT_EXTENSION", vs.mProjectExtension);
        std::string dstFile = Poco::replace<std::string>(srcFile, "Sample", inProjectName);
        CreateTemplateOffspring(srcFile, inProjectName);
        Poco::File oldFile(srcFile);
        oldFile.remove();
    }
}


int main(int argc, char ** argv)
{
    try
    {
        std::string projectName;
        if (argc <= 1)
        {
            std::cout << "Please type a name for the project followed by ENTER:" << std::endl;
            std::cin >> projectName;
        }
        else
        {
            projectName = argv[1];
        }


        gVSVersions.push_back(VSVersion("2005", "vcproj", "sln", "vs80"));
        gVSVersions.push_back(VSVersion("2008", "vcproj", "sln", "vs90"));
        gVSVersions.push_back(VSVersion("2010", "vcxproj", "sln", "vs100"));

        CreateSolutionFile(projectName);
        CreateSolutionDirectory(projectName);
    }
    catch (const Poco::Exception & exc)
    {
        std::cout << "Poco exception thrown: " << exc.message() << std::endl;
    }
    catch (const std::exception & exc)
    {
        std::cout << "Stl exception thrown: " <<  exc.what() << std::endl;
    }
    catch (...)
    {
        std::cout << "Exception of mysterious thrown. Exiting." << std::endl;
    }  

    std::cout << "\nPress ENTER to quit.";
    char c;
    std::cin >> c;
    return 0;
}
